package io.github.ali.commons.lock.impl;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import io.github.ali.commons.lock.Locker;
import io.github.ali.commons.lock.exception.LockException;


/**
 * 分布式锁抽象实现
 *
 * @author fuli
 * @version 1.0.0
 * @date 2018年9月18日
 */
public abstract class AbstractLocker implements Locker {
	private static Logger LOGGER = LoggerFactory.getLogger(AbstractLocker.class);
    protected int defaultTryTimes;
    protected long defaultTryGap;
    protected long defaultExpiring;
    /**
     * 尝试次数上限;即使设置tryTimes=-1也不超过此值
     */
    protected int maxTryTimes;

    public AbstractLocker() {
        defaultTryTimes = 1;
        defaultTryGap = 100L;
        defaultExpiring = 0L;
        maxTryTimes = 10000;
    }

    @Override
    public boolean tryLock(String key, String value, long expiring, int tryTimes, long tryGap) {
        try {
            lock(key, value, expiring, tryTimes, tryGap);
            return true;
        } catch (Exception e) {
        	if(LOGGER.isDebugEnabled()) {
        		LOGGER.debug("获取分布式锁异常,key:" + key, e);
        	}
        }
        return false;
    }

    @Override
    public void lock(String key, String value, long expiring, int tryTimes, long tryGap) throws LockException{
        try {
            expiring = expiring == 0 ? defaultExpiring : expiring;
            tryTimes = tryTimes == 0 ? defaultTryTimes : tryTimes;
            tryTimes = tryTimes > maxTryTimes ? maxTryTimes : tryTimes;
            tryGap = tryGap == 0L ? defaultTryGap : tryGap;
            do {
                tryTimes--;
                try {
                    doLock(key, value, expiring);
                    tryTimes = 0;
                } catch (Exception e) {
                    if (tryTimes == 0 || tryTimes < -maxTryTimes) {
                        throw e;
                    }
                    if (tryGap > 0L) {
                        try {
                            Thread.sleep(tryGap);
                        } catch (Exception ie) {
                            LOGGER.error("获取分布式锁" + key + "休眠异常!", ie);
                        }
                    }
                }
            } while (tryTimes != 0);
        } catch (Exception e) {
            throw new LockException("LOCK_FAILED : "+e.getMessage(), e);
        }
    }

    @Override
    public void unlock(String key, String value) {
        try {
            doUnlock(key, value);
        } catch (Exception e) {
			LOGGER.error(String.format("释放分布式锁异常,key : %s , error : %s", key,e.toString()));
        }
    }

    /**
     * 执行加锁
     * @param key
     * @param value
     * @param expiring 单位：秒
     * @throws Exception
     */
    abstract protected void doLock(String key, String value, long expiring) throws Exception;

    abstract protected void doUnlock(String key, String value) throws Exception;

    public void setDefaultTryTimes(int defaultTryTimes) {
        this.defaultTryTimes = defaultTryTimes;
    }

    public void setDefaultTryGap(long defaultTryGap) {
        this.defaultTryGap = defaultTryGap;
    }

    public void setDefaultExpiring(long defaultExpiring) {
        this.defaultExpiring = defaultExpiring;
    }

    public void setMaxTryTimes(int maxTryTimes) {
        this.maxTryTimes = maxTryTimes;
    }
}
